<script lang="ts" setup>
import type { CSSProperties } from 'vue'
import { computed, getCurrentInstance, inject, onBeforeMount, provide, reactive, unref } from 'vue'
import { Popover } from 'ant-design-vue'
import type { SubMenuProvider } from './types'
import { useMenuItem } from './useMenu'
import { useSimpleRootMenuContext } from './useSimpleMenuContext'
import { useDesign } from '@/hooks/web/useDesign'
import { propTypes } from '@/utils/propTypes'
import { CollapseTransition } from '@/components/Transition'
import { Icon } from '@/components/Icon'
import { isBoolean, isObject } from '@/utils/is'
import { mitt } from '@/utils/mitt'

defineOptions({ name: 'SubMenu' })

const props = defineProps({
  name: {
    type: [String, Number] as PropType<string | number>,
    required: true,
  },
  disabled: propTypes.bool,
  collapsedShowTitle: propTypes.bool,
})
const DELAY = 200
const instance = getCurrentInstance()

const state = reactive({
  active: false,
  opened: false,
})

const data = reactive({
  timeout: null as TimeoutHandle | null,
  mouseInChild: false,
  isChild: false,
})

const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } = useMenuItem(instance)

const { prefixCls } = useDesign('menu')

const subMenuEmitter = mitt()

const { rootMenuEmitter } = useSimpleRootMenuContext()

const {
  addSubMenu: parentAddSubmenu,
  removeSubMenu: parentRemoveSubmenu,
  removeAll: parentRemoveAll,
  getOpenNames: parentGetOpenNames,
  isRemoveAllPopup,
  sliceIndex,
  level,
  props: rootProps,
  handleMouseleave: parentHandleMouseleave,
} = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!

const getClass = computed(() => {
  return [
    `${prefixCls}-submenu`,
    {
      [`${prefixCls}-opened`]: state.opened,
      [`${prefixCls}-submenu-disabled`]: props.disabled,
      [`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu),
      [`${prefixCls}-child-item-active`]: state.active,
    },
  ]
})

const getAccordion = computed(() => rootProps.accordion)
const getCollapse = computed(() => rootProps.collapse)
const getTheme = computed(() => rootProps.theme)

const getOverlayStyle = computed((): CSSProperties => {
  return {
    minWidth: '200px',
  }
})

const getIsOpend = computed(() => {
  const name = props.name
  if (unref(getCollapse))
    return parentGetOpenNames().includes(name)

  return state.opened
})

const getSubClass = computed(() => {
  const isActive = rootProps.activeSubMenuNames.includes(props.name)
  return [
    `${prefixCls}-submenu-title`,
    {
      [`${prefixCls}-submenu-active`]: isActive,
      [`${prefixCls}-submenu-active-border`]: isActive && level === 0,
      [`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0,
    },
  ]
})

function getEvents(deep: boolean) {
  if (!unref(getCollapse))
    return {}

  return {
    onMouseenter: handleMouseenter,
    onMouseleave: () => handleMouseleave(deep),
  }
}

function handleClick() {
  const { disabled } = props
  if (disabled || unref(getCollapse))
    return
  const opened = state.opened

  if (unref(getAccordion)) {
    const { uidList } = getParentList()
    rootMenuEmitter.emit('on-update-opened', {
      opend: false,
      parent: instance?.parent,
      uidList,
    })
  }
  else {
    rootMenuEmitter.emit('open-name-change', {
      name: props.name,
      opened: !opened,
    })
  }
  state.opened = !opened
}

function handleMouseenter() {
  const disabled = props.disabled
  if (disabled)
    return

  subMenuEmitter.emit('submenu:mouse-enter-child')

  const index = parentGetOpenNames().findIndex(item => item === props.name)

  sliceIndex(index)

  const isRoot = level === 0 && parentGetOpenNames().length === 2
  if (isRoot)
    parentRemoveAll()

  data.isChild = parentGetOpenNames().includes(props.name)
  clearTimeout(data.timeout!)
  data.timeout = setTimeout(() => {
    parentAddSubmenu(props.name)
  }, DELAY)
}

function handleMouseleave(deepDispatch = false) {
  const parentName = getParentMenu.value?.props.name
  if (!parentName)
    isRemoveAllPopup.value = true

  if (parentGetOpenNames().slice(-1)[0] === props.name)
    data.isChild = false

  subMenuEmitter.emit('submenu:mouse-leave-child')
  if (data.timeout) {
    clearTimeout(data.timeout!)
    data.timeout = setTimeout(() => {
      if (isRemoveAllPopup.value)
        parentRemoveAll()
      else if (!data.mouseInChild)
        parentRemoveSubmenu(props.name)
    }, DELAY)
  }
  if (deepDispatch) {
    if (getParentSubMenu.value)
      parentHandleMouseleave?.(true)
  }
}

onBeforeMount(() => {
  subMenuEmitter.on('submenu:mouse-enter-child', () => {
    data.mouseInChild = true
    isRemoveAllPopup.value = false
    clearTimeout(data.timeout!)
  })
  subMenuEmitter.on('submenu:mouse-leave-child', () => {
    if (data.isChild)
      return
    data.mouseInChild = false
    clearTimeout(data.timeout!)
  })

  rootMenuEmitter.on('on-update-opened', (data: boolean | (string | number)[] | Recordable) => {
    if (unref(getCollapse))
      return
    if (isBoolean(data)) {
      state.opened = data
      return
    }
    if (isObject(data) && rootProps.accordion) {
      const { opend, parent, uidList } = data as Recordable
      if (parent === instance?.parent)
        state.opened = opend
      else if (!uidList.includes(instance?.uid))
        state.opened = false

      return
    }

    if (props.name && Array.isArray(data))
      state.opened = (data as (string | number)[]).includes(props.name)
  })

  rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => {
    if (instance?.uid)
      state.active = data.includes(instance?.uid)
  })
})

function handleOpenChange(open: boolean) {
  state.opened = open
}

// provide
provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
  addSubMenu: parentAddSubmenu,
  removeSubMenu: parentRemoveSubmenu,
  getOpenNames: parentGetOpenNames,
  removeAll: parentRemoveAll,
  isRemoveAllPopup,
  sliceIndex,
  level: level + 1,
  handleMouseleave,
  props: rootProps,
})
</script>

<template>
  <li :class="getClass">
    <template v-if="!getCollapse">
      <div :class="`${prefixCls}-submenu-title`" :style="getItemStyle" @click.stop="handleClick">
        <slot name="title" />
        <Icon icon="eva:arrow-ios-downward-outline" :size="14" :class="`${prefixCls}-submenu-title-icon`" />
      </div>
      <CollapseTransition>
        <ul v-show="state.opened" :class="prefixCls">
          <slot />
        </ul>
      </CollapseTransition>
    </template>

    <Popover
      v-else
      placement="right"
      :overlay-class-name="`${prefixCls}-menu-popover`"
      :open="getIsOpend"
      :overlay-style="getOverlayStyle"
      :align="{ offset: [0, 0] }"
      @open-change="handleOpenChange"
    >
      <div :class="getSubClass" v-bind="getEvents(false)">
        <div
          :class="[
            {
              [`${prefixCls}-submenu-popup`]: !getParentSubMenu,
              [`${prefixCls}-submenu-collapsed-show-tit`]: collapsedShowTitle,
            },
          ]"
        >
          <slot name="title" />
        </div>
        <Icon v-if="getParentSubMenu" icon="eva:arrow-ios-downward-outline" :size="14" :class="`${prefixCls}-submenu-title-icon`" />
      </div>
      <!-- eslint-disable-next-line -->
      <template #content v-show="state.opened">
        <div v-bind="getEvents(true)">
          <ul :class="[prefixCls, `${prefixCls}-${getTheme}`, `${prefixCls}-popup`]">
            <slot />
          </ul>
        </div>
      </template>
    </Popover>
  </li>
</template>
